package au.edu.qut.bpm.genbinder;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import org.deckfour.xes.in.XParser;
import org.deckfour.xes.in.XesXmlParser;
import org.deckfour.xes.model.XLog;
import org.deckfour.xes.out.XSerializer;
import org.deckfour.xes.out.XesXmlSerializer;
import org.uncommons.maths.random.DiscreteUniformGenerator;
import org.uncommons.maths.random.MersenneTwisterRNG;
import org.uncommons.maths.random.PoissonGenerator;
import org.uncommons.maths.random.Probability;
import org.uncommons.swing.SwingBackgroundTask;
import org.uncommons.watchmaker.framework.EvolutionEngine;
import org.uncommons.watchmaker.framework.EvolutionObserver;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;
import org.uncommons.watchmaker.framework.GenerationalEvolutionEngine;
import org.uncommons.watchmaker.framework.PopulationData;
import org.uncommons.watchmaker.framework.SelectionStrategy;
import org.uncommons.watchmaker.framework.operators.EvolutionPipeline;
import org.uncommons.watchmaker.framework.selection.TournamentSelection;
import org.uncommons.watchmaker.framework.termination.ElapsedTime;
import org.uncommons.watchmaker.framework.termination.GenerationCount;
import org.uncommons.watchmaker.framework.termination.TargetFitness;
import org.uncommons.watchmaker.swing.AbortControl;
import org.uncommons.watchmaker.swing.ProbabilityParameterControl;

import au.edu.qut.bpm.genbinder.genetic.crossover.LogActivitiesCrossover;
import au.edu.qut.bpm.genbinder.genetic.crossover.LogActivitiesCrossoverUnsafeRepaired;
import au.edu.qut.bpm.genbinder.genetic.evaluation.LogEvaluator;
import au.edu.qut.bpm.genbinder.genetic.evaluation.costmodels.MultiObjectiveCostModel;
import au.edu.qut.bpm.genbinder.genetic.genesis.InitialIdenticalLogPopulationFactory;
import au.edu.qut.bpm.genbinder.genetic.genesis.InitialLogPopulationFactory;
import au.edu.qut.bpm.genbinder.genetic.genesis.LogAbstractFactory;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogDuplicateLog;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogNewGreedySolution;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogNewRandomSolution;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogResourceMutation;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogResourceMutationUnsafeRepaired;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogTimeShiftMutation;
import au.edu.qut.bpm.genbinder.genetic.mutation.LogTimeShiftMutationUnsafeRepaired;
import au.edu.qut.bpm.genbinder.genetic.other.LogRepairer;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;
import au.edu.qut.bpm.genbinder.models.Resource;
import au.edu.qut.bpm.genbinder.ui.CaseListPanel;
import au.edu.qut.bpm.genbinder.ui.MainPanel;
import au.edu.qut.bpm.genbinder.util.DiscreteLogConverter;
import au.edu.qut.bpm.genbinder.util.LogToXESConverter;
import au.edu.qut.bpm.genbinder.util.XESToLogConverter;

public class GeneticBinder {
	// Configuration settings: population size, to discretize or not and duration of one discretized block

	// TODO: Abstract away
	public static final int POPULATION_SIZE = 50;
	public static final int ELITE_COUNT = (int) Math.round(GeneticBinder.POPULATION_SIZE * 0.05);
	public static final boolean DISCRETIZE = true;
	public static final long MINUTE = (1000 * 60);
	public static long BLOCK_DURATION = MINUTE * 1;
	
	private static String strOperatorType = "safe";					//safe, repair, unsafe, bruteforce, heuristic, duplicate
	public static String strLogType = "normal";						//normal, doublecase, doubletask
//	public static String strFileNameExtension = "_1000RS1";			//extensions given to the file names to avoid overwriting
//	public static String strFileNameExtension = "_500RS1";			//extensions given to the file names to avoid overwriting
//	public static String strFileNameExtension = "_1000WT1";			//extensions given to the file names to avoid overwriting
//	public static String strFileNameExtension = "_500WT1";			//extensions given to the file names to avoid overwriting
//	public static String strFileNameExtension = "_WT1";			//extensions given to the file names to avoid overwriting
	public static String strFileNameExtension = "_RS1";			//extensions given to the file names to avoid overwriting
	private static int intSafePercentage = 100;							//the percentage of safe logs in the initial log generation
	private static double costPerViolation = 455.0;					//the cost per violation
	private static String strTerminationCondition = "generationcount";	//the termination condition for the genetic algorithm
	private static int intCount = 10000;									//generation or duration count (in minutes) (1 day = 1440 minutes)
	
	///*
	private static int experimentCount = 1;
	private static String[] strOperatorTypeArray = {"safe", "safe", "safe", "safe", "safe"};
	public static String[] strLogTypeArray = {"normal", "normal", "normal", "normal", "normal"};
//	public static String[] strFileNameExtensionArray = {"_1000RS1", "_1000RS2", "_1000RS3", "_1000RS4", "_1000RS5"};
//	public static String[] strFileNameExtensionArray = {"_500RS1", "_500RS2", "_500RS3", "_500RS4", "_500RS5"};
//	public static String[] strFileNameExtensionArray = {"_1000WT1", "_1000WT2", "_1000WT3", "_1000WT4", "_1000WT5"};
//	public static String[] strFileNameExtensionArray = {"_500WT1", "_500WT2", "_500WT3", "_500WT4", "_500WT5"};
//	public static String[] strFileNameExtensionArray = {"_WT1", "_WT2", "_WT3", "_WT4", "_WT5"};
	public static String[] strFileNameExtensionArray = {"_RS1", "_RS2", "_RS3", "_RS4", "_RS5"};
	private static int[] intSafePercentageArray = {100, 100, 100, 100, 100};				
	private static double[] costPerViolationArray = {455.0, 455.0, 455.0, 455.0, 455.0};
	
	private static int finalExperimentCount = 5;
	
	private static String logFileLocation = "logs/CarInsuranceClaimCPNV2_Converted_Filtered.xes";
	//*/
	
	/*
	private static int experimentCount = 1;
	private static String[] strOperatorTypeArray = {"safe", "safe", "safe", "safe", "safe", "unsafe", "unsafe", "unsafe", "unsafe", "unsafe", "repair", "repair", "repair", "repair", "repair",
													"heuristic", "heuristic", "heuristic", "heuristic", "heuristic", "bruteforce", "bruteforce", "bruteforce", "bruteforce", "bruteforce",
													"safe", "safe", "safe", "safe", "safe", "unsafe", "unsafe", "unsafe", "unsafe", "unsafe", "repair", "repair", "repair", "repair", "repair",
													"heuristic", "heuristic", "heuristic", "heuristic", "heuristic", "bruteforce", "bruteforce", "bruteforce", "bruteforce", "bruteforce",
													"safe", "safe", "safe", "safe", "safe", "unsafe", "unsafe", "unsafe", "unsafe", "unsafe", "repair", "repair", "repair", "repair", "repair",
													"heuristic", "heuristic", "heuristic", "heuristic", "heuristic", "bruteforce", "bruteforce", "bruteforce", "bruteforce", "bruteforce"};
	public static String[] strLogTypeArray = {"normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal",
												"normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal",
												"doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase",
												"doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase", "doublecase",
												"doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask",
												"doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask", "doubletask"};
	public static String[] strFileNameExtensionArray = {"_NLSafe1", "_NLSafe2", "_NLSafe3", "_NLSafe4", "_NLSafe5", "_NLUnsafe1", "_NLUnsafe2", "_NLUnsafe3", "_NLUnsafe4", "_NLUnsafe5", "_NLRepair1", "_NLRepair2", "_NLRepair3", "_NLRepair4", "_NLRepair5",
														"_NLHeuristic1", "_NLHeuristic2", "_NLHeuristic3", "_NLHeuristic4", "_NLHeuristic5", "_NLBruteForce1", "_NLBruteForce2", "_NLBruteForce3", "_NLBruteForce4", "_NLBruteForce5",
														"_DCSafe1", "_DCSafe2", "_DCSafe3", "_DCSafe4", "_DCSafe5", "_DCUnsafe1", "_DCUnsafe2", "_DCUnsafe3", "_DCUnsafe4", "_DCUnsafe5", "_DCRepair1", "_DCRepair2", "_DCRepair3", "_DCRepair4", "_DCRepair5",
														"_DCHeuristic1", "_DCHeuristic2", "_DCHeuristic3", "_DCHeuristic4", "_DCHeuristic5", "_DCBruteForce1", "_DCBruteForce2", "_DCBruteForce3", "_DCBruteForce4", "_DCBruteForce5",
														"_DTSafe1", "_DTSafe2", "_DTSafe3", "_DTSafe4", "_DTSafe5", "_DTUnsafe1", "_DTUnsafe2", "_DTUnsafe3", "_DTUnsafe4", "_DTUnsafe5", "_DTRepair1", "_DTRepair2", "_DTRepair3", "_DTRepair4", "_DTRepair5",
														"_DTHeuristic1", "_DTHeuristic2", "_DTHeuristic3", "_DTHeuristic4", "_DTHeuristic5", "_DTBruteForce1", "_DTBruteForce2", "_DTBruteForce3", "_DTBruteForce4", "_DTBruteForce5"};			
	private static int[] intSafePercentageArray = {100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
													100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
													100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100};				
	private static double[] costPerViolationArray = {1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0, 1770.0,
														3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0, 3950.0,
														455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0, 455.0};					
	//*/
	
	
	//private static String[] strTerminationConditionArray = "generationcount";	
	//private static int intCountArray = 4;									
	
	
	@SuppressWarnings("unused")
	private static MainPanel mainPanel;
	public static ConcreteEventLog bestCandidateLog = null;

	public static void main(String[] args) throws Exception {
//		if (args.length > 0) {
//			strOperatorType = args[0];
//			strLogType = args[1];
//			strFileNameExtension = args[2];
//			intSafePercentage = Integer.parseInt(args[3]);
//			costPerViolation = Double.parseDouble(args[4]);
//			strTerminationCondition = args[5];
//			intCount = Integer.parseInt(args[6]);
//			finalExperimentCount = Integer.parseInt(args[7]);
//		}
		XParser parser = new XesXmlParser();
		List<XLog> l = null;
		
		switch (strLogType) {
			case "normal":
				l = parser.parse(new File(logFileLocation));
				break;
			case "lowerwaittime":
				l = parser.parse(new File("logs/CarInsuranceClaim(200Cases).xes"));
				break;
			case "supervisorgeneralisation":
				l = parser.parse(new File("logs/CarInsuranceClaim(DoubleTasks).xes"));
				break;
			default:
				l = parser.parse(new File("logs/CarInsuranceClaim(100Cases).xes"));
		}

		ConcreteEventLog log = XESToLogConverter.createLog(l.get(0));

		if (DISCRETIZE) {
			// Where to start the first block?
			// (1) Either the lowest closest (e.g. 9:10 when first activity starts at 9:14)
			long zeroBlock = DiscreteLogConverter.timeToLowerBlock(log.getLogArrival(), 0, BLOCK_DURATION) * BLOCK_DURATION;

			// (2) Or the first arrival as is (e.g. 9:14)
			// long zeroBlock = log.getLogArrival();

			// (3) Or your own start position
			// long zeroBlock = 1366.840800000;

			System.out.println("Discretizing log. First block starting at: " + new Date(zeroBlock) + "\nUsing " + (BLOCK_DURATION / 60000) + " minute block increments");

			log = DiscreteLogConverter.makeDiscrete(log, zeroBlock, BLOCK_DURATION);

			CaseListPanel.TIMETOPIXELS = 30;		//block width per minute
			CaseListPanel.ZEROBLOCK = zeroBlock;
		} else {
			// Make the UI a bit more sensible // TODO: Should also be done separately
			CaseListPanel.TIMETOPIXELS = 1d / ((double) BLOCK_DURATION / 30d);
			CaseListPanel.ZEROBLOCK = 0L;
			BLOCK_DURATION = 1;
		}
		bestCandidateLog = log;
		mainPanel = new MainPanel();
		
		/*
		JFrame frame = new JFrame("GenBinder Prototype");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(mainPanel, BorderLayout.CENTER);
		frame.pack();
		frame.setVisible(true);
		frame.setSize(new Dimension(1200, 200));
		MainPanel.showLog(log);
		//*/
	}

	public static SwingBackgroundTask<ConcreteEventLog> createTask(final ConcreteEventLog log, final EvolutionObserver<ConcreteEventLog> observer,	final AbortControl abortControl) throws IOException {
		// The call function for the GA
		// TODO: Should also be put in separate classes and so on
		return new SwingBackgroundTask<ConcreteEventLog>() {
//			String filename = "logs/GenerationOutput" + strFileNameExtension + ".xls";
//			WritableWorkbook workbook = Workbook.createWorkbook(new File(filename));
//			WritableSheet sheet = workbook.createSheet("First Sheet", 0);
			
			int numberOfCases = log.getAbstractLog().getCases().size();
			int numberOfActivities = log.getAllActivity().size();
			int numberOfActInCase = (strLogType.equalsIgnoreCase("doubletask")) ? (log.getAbstractLog().getTasks().size() - 4)	: (log.getAbstractLog().getTasks().size() - 2);
			
			@Override
			protected ConcreteEventLog performTask() {
				System.out.println("Starting " + strOperatorType + " " + strLogType + " " + strFileNameExtension + " " + intSafePercentage + " " + costPerViolation + " " + strTerminationCondition + " " + intCount);
				//System.out.println("Start Util = " + new Date().toString());
				calcResourceUtil();
				//System.out.println("Start Population = " + new Date().toString());
				Random rng = new MersenneTwisterRNG();
				// GA engine components
				LogAbstractFactory populationFactory = new InitialIdenticalLogPopulationFactory(log);
				//LogAbstractFactory populationFactory = new LogGreedyFactory(log.getAbstractLog());
//				LogAbstractFactory populationFactory = new InitialLogPopulationFactory(log, intSafePercentage);
				
				ProbabilityParameterControl selectionPressure = new ProbabilityParameterControl(Probability.EVENS, Probability.ONE, 2, new Probability(0.75d));
				SelectionStrategy<Object> selectionStrategy = new TournamentSelection(selectionPressure.getNumberGenerator()); //RankSelection(),StochasticUniversalSampling(),RouletteWheelSelection(),TruncationSelection(0.25d), SigmaScaling(),TournamentSelection(selectionPressure.getNumberGenerator())
				LogEvaluator fitnessFunction = new MultiObjectiveCostModel(costPerViolation); //Set cost model here
				// TODO: All these parameters should be neatly abstracted away in separate class

				List<EvolutionaryOperator<ConcreteEventLog>> operators = new ArrayList<EvolutionaryOperator<ConcreteEventLog>>();
				
				// Add your operators here: mutations, crossover, repairing...
				switch (strOperatorType) {
					case "safe":
						operators.add(new LogActivitiesCrossover(true, (int) (numberOfActivities * 0.01)));
						operators.add(new LogResourceMutation(true, new PoissonGenerator((numberOfCases * 0.10), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						operators.add(new LogTimeShiftMutation(true, new PoissonGenerator((numberOfCases * 1.00), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						
//						operators.add(new LogNewGreedySolution(true, 0.05));
						break;
					case "unsafe":
						operators.add(new LogActivitiesCrossover(false, new PoissonGenerator((numberOfActivities * 0.20), rng)));
						operators.add(new LogResourceMutation(false, new PoissonGenerator((numberOfCases * 0.05), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						operators.add(new LogTimeShiftMutation(false, new PoissonGenerator((numberOfCases * 0.10), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						
						operators.add(new LogNewGreedySolution(true, 0.05));
						break;
					case "repair":
						operators.add(new LogActivitiesCrossoverUnsafeRepaired(new PoissonGenerator((numberOfActivities * 0.50), rng)));
						operators.add(new LogResourceMutationUnsafeRepaired(new PoissonGenerator((numberOfCases * 0.10), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						operators.add(new LogTimeShiftMutationUnsafeRepaired(new PoissonGenerator((numberOfCases * 1.00), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						
						operators.add(new LogNewGreedySolution(true, 0.05));
						break;
					case "heuristic":
						operators.add(new LogNewGreedySolution(true, 1));
						break;
					case "bruteforce":
						operators.add(new LogNewRandomSolution(false, 1));
						break;
					case "duplicate":
						operators.add(new LogDuplicateLog());
						break;
					case "repaironly":
						operators.add(new LogRepairer());
						break;
					default:
						operators.add(new LogActivitiesCrossover(true, (int) (numberOfActivities * 0.01)));
						operators.add(new LogResourceMutation(true, new PoissonGenerator((numberOfCases * 0.10), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						operators.add(new LogTimeShiftMutation(true, new PoissonGenerator((numberOfCases * 1.00), rng), new DiscreteUniformGenerator(1, numberOfActInCase, rng)));
						
						operators.add(new LogNewGreedySolution(true, 0.05));
				}					

				EvolutionaryOperator<ConcreteEventLog> pipeline = new EvolutionPipeline<ConcreteEventLog>(operators);
				// Create the GA engine
				EvolutionEngine<ConcreteEventLog> engine = new GenerationalEvolutionEngine<ConcreteEventLog>(populationFactory, pipeline, fitnessFunction, selectionStrategy, rng);
				engine.addEvolutionObserver(observer);
				
				engine.addEvolutionObserver(new EvolutionObserver<ConcreteEventLog>()
				{
					public void populationUpdate(PopulationData<? extends ConcreteEventLog> data)
					{	/*
						try {
							Label label1 = new Label(0, sheet.getRows(), (data.getGenerationNumber() + 1 + ""));
							Label label2 = new Label(1, sheet.getRows(), (String.format("%.5f", data.getBestCandidateFitness()) + ""));
							Label label3 = new Label(2, sheet.getRows(), (new Date().toString()));
							Label label4 = new Label(3, sheet.getRows(), data.getBestCandidate().getLogEnd() + "");
							Label label5 = new Label(4, sheet.getRows(), data.getBestCandidate().isLogSafe() + " " + data.getBestCandidate().isLogValid());
							Label label6 = new Label(5, sheet.getRows(), (String.format("%.5f", data.getMeanFitness()) + ""));
							
							sheet.addCell(label1);
							sheet.addCell(label2);
							sheet.addCell(label3);
							sheet.addCell(label4);
							sheet.addCell(label5);
							sheet.addCell(label6);
						} catch (WriteException e) {
							e.printStackTrace();
						}
						//*/
						if ((data.getGenerationNumber() % 50) == 0) {
							bestCandidateLog = data.getBestCandidate();
							System.out.println("Start Export = " + new Date().toString());
							saveXESFile(data.getGenerationNumber() + "");
							System.out.println("End = " + new Date().toString());
						}

						String s = (data.getGenerationNumber() + 1) + " " + 
									String.format("%.5f", data.getBestCandidateFitness()) + " " + 
									new Date().toString() + " " + 
									data.getBestCandidate().getLogEnd() + " " + 
									data.getBestCandidate().isLogSafe() + " " + 
									data.getBestCandidate().isLogValid() + " " + 
									String.format("%.5f", data.getMeanFitness());
						
						try
						{
						    String filename= "logs/GenerationOutput" + strFileNameExtension + ".txt";
						    FileWriter fw = new FileWriter(filename,true); //the true will append the new data
						    fw.write(s + System.getProperty("line.separator"));//appends the string to the file
						    fw.close();
						}
						catch(IOException ioe)
						{
						    System.err.println("IOException: " + ioe.getMessage());
						}
					
						System.out.printf("Gen. %d: %s\n", (data.getGenerationNumber() + 1), data.getBestCandidateFitness());
					}
				});
				// Let's g0...
				
				if (strTerminationCondition.equalsIgnoreCase("generationcount")) {
					//System.out.println("generation count!");
					return engine.evolve(
							POPULATION_SIZE,
							ELITE_COUNT,
							new GenerationCount(intCount),
							abortControl.getTerminationCondition() // Or when user aborts
					);
				} else if (strTerminationCondition.equalsIgnoreCase("elapsedtime")) {
					//System.out.println("elapsed time!");
					return engine.evolve(
							POPULATION_SIZE,
							ELITE_COUNT,
							new ElapsedTime(MINUTE * intCount), //new ElapsedTime(MINUTE * 10080)
							abortControl.getTerminationCondition() // Or when user aborts
					);
				} else if (strTerminationCondition.equalsIgnoreCase("targetfitness")) {
					//System.out.println("target fitness!");
					return engine.evolve(
							POPULATION_SIZE,
							ELITE_COUNT,
							new TargetFitness(intCount, false),
							abortControl.getTerminationCondition() // Or when user aborts
					);
				} else {
					return engine.evolve(
							POPULATION_SIZE,
							ELITE_COUNT,
							new GenerationCount(intCount),
							abortControl.getTerminationCondition() // Or when user aborts
					);
				}
			}

			@Override
			protected void postProcessing(ConcreteEventLog result) {
				System.out.println("Done: " + new Date().toString());
				/*
				try {
					workbook.write();
					workbook.close();
				} catch (IOException | WriteException e) {
					e.printStackTrace();
				}//*/
				System.out.println("Start End Util = " + new Date().toString());
				calcResourceUtil();
				System.out.println("Start Export = " + new Date().toString());
				saveXESFile("");
				System.out.println("End All = " + new Date().toString());
				///*
				if (experimentCount <= finalExperimentCount) {
					strOperatorType = strOperatorTypeArray[experimentCount];
					strLogType = strLogTypeArray[experimentCount];
					strFileNameExtension = strFileNameExtensionArray[experimentCount];
					intSafePercentage = intSafePercentageArray[experimentCount];
					costPerViolation = costPerViolationArray[experimentCount];
					
					XParser parser = new XesXmlParser();
					List<XLog> l = null;
					try {
						switch (strLogType) {
						case "normal":
							l = parser.parse(new File(logFileLocation));
							break;
						case "doublecase":
							l = parser.parse(new File("logs/CarInsuranceClaim(200Cases).xes"));
							break;
						case "doubletask":
							l = parser.parse(new File("logs/CarInsuranceClaim(DoubleTasks).xes"));
							break;
						default:
							l = parser.parse(new File("logs/CarInsuranceClaim(100Cases).xes"));
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					ConcreteEventLog log = null;
					try {
						log = XESToLogConverter.createLog(l.get(0));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					long zeroBlock = DiscreteLogConverter.timeToLowerBlock(log.getLogArrival(), 0, BLOCK_DURATION) * BLOCK_DURATION;

					log = DiscreteLogConverter.makeDiscrete(log, zeroBlock, BLOCK_DURATION);

					CaseListPanel.TIMETOPIXELS = 30;		//block width per minute
					CaseListPanel.ZEROBLOCK = zeroBlock;

					bestCandidateLog = log;
					mainPanel = new MainPanel();
					experimentCount++;
				}
				//*/
			}
			
			protected void saveXESFile(String extraFileName) {
				try {
					File xesFile = new File("logs/XESLog" + strFileNameExtension + extraFileName + ".xes");
					XLog xesLog = LogToXESConverter.createLog(bestCandidateLog, CaseListPanel.ZEROBLOCK, GeneticBinder.BLOCK_DURATION);
					XSerializer serializer = new XesXmlSerializer();
					OutputStream oStream = new BufferedOutputStream(new FileOutputStream(xesFile));
					serializer.serialize(xesLog, oStream);
					System.out.println("Exported " + "logs/XESLog" + strFileNameExtension + ".xes XES log file.");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			protected void calcResourceUtil() {
				long HORIZON = 60;
				String str = "";
				for (int a = 0; a <= bestCandidateLog.getLogEnd(); a++) {
					str = a + " ";
					
					for (Resource r : bestCandidateLog.getAbstractLog().getResources()) {
						long HORIZONSTART = a - 60;
						
						double resourceUtilCount = 0, resourceUtilization = 0;
						
						resourceUtilCount = bestCandidateLog.getResourceUtilization(r, HORIZON, HORIZONSTART);
						
						if (HORIZONSTART < 0) {
							resourceUtilization = 0.8;
						} else {
							resourceUtilization = (1.0 / HORIZON) * resourceUtilCount;
						}
						
						str += r.getId() + " " + String.format("%.5f", resourceUtilization) + " ";
					}
					try
					{
					    String filename= "logs/ResUtil" + strFileNameExtension + ".txt";
					    FileWriter fw = new FileWriter(filename,true); //the true will append the new data
					    fw.write(str + System.getProperty("line.separator"));//appends the string to the file
					    fw.close();
					}
					catch(IOException ioe)
					{
					    System.err.println("IOException: " + ioe.getMessage());
					}
				}
			}
			
		};
	}
}
